using System;
using System.Reflection;
using System.IO;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Collections;
using System.Collections.Generic;
using Microsoft.BizTalk.ScalableTransformation;


namespace ParsingLogic
{
    public class CodeGenDriver
    {
        //private const string outputDir = "..\\..\\GeneratedOutput";
        private string outputDir = "";
        private string xsltDir = "..\\..\\XSLTs";
        //private const string xmlMetaDataDir = "..\\..\\MetaData";
        private string xmlMetaDataDir = "";

        public CodeGenDriver(string outputDirectory, string xsltDirectory, string XMLMetaDataDirectory)
        {
            outputDir = outputDirectory;
            xsltDir = xsltDirectory;
            xmlMetaDataDir = XMLMetaDataDirectory;
        }


        public void GenerateCode(bool UseStreamingXSL, string DataMapLocation, string PartialAssemblyName, string CompiledMapName, string MapContext, string UploadContextMapName, bool AllMessageMap, string Namespace, string ActivityName, string SourceSchemaNameSpace)
        {
            #region Variables
            bool useXSL = UseStreamingXSL;
            string inputDocName = DataMapLocation;
            string outputNameSpace = Namespace;

            string contextMapName = MapContext;
            string contextOnlyMapName = "";
            string contextOnlyActivityName = "";
            string uploadContextMapName = UploadContextMapName;

            const string dispatcherMetadataFile = "Dispatcher_Generated_Metadata.xml";
            const string allActivitiesMetadataFile = "All_MessageTypes_Generated_Metadata.xml";
            const string contextLibraryMetadataFile = "MetaData_ContextNamespaces.xml";

         

            if (AllMessageMap)
            {
                contextOnlyMapName = MapContext;
                contextOnlyActivityName = ActivityName;
                contextMapName = "";
                inputDocName = "";
            }

            #endregion

            Types.BTSMessageType _newDispatcherMessage = new Types.BTSMessageType();
            
            //holder for new bam activities defined in this code gen run
            List<Types.BAMActivity> _newBAMActivities = new List<ParsingLogic.Types.BAMActivity>();
            //Individual BAM Activity
            Types.BAMActivity _newBamActivity;
            
            #region Populate the Activities from the supplied maps - BUILD _activitiesFromMap
            #region Work on an inbound datamap.  Get Activity list by reading the inbound data BTM
            //Read out the activity list from the map
            System.Collections.ArrayList _activitiesFromMap = new ArrayList();
            if (inputDocName != "" && inputDocName != null)
            {
                _activitiesFromMap = BTS_MapReader.MapReader.GetActivityList(inputDocName);
            }
            #endregion
            #region Work on inbound context map.  Get Activity list by reading the inbound context BTM
            bool hasCTX = false;
            bool hasCTXOnly = false;
            if (contextMapName != "")
            {
                foreach (string str in BTS_MapReader.MapReader.GetActivityList(contextMapName))
                {
                    hasCTX = true;
                    if (!_activitiesFromMap.Contains(str))
                    {
                        _activitiesFromMap.Add(str);
                    }
                }
            }
            #endregion
            #region Work on inbound context ONLY map.  The Activity Name is supplied for a Context Only map.
            if (contextOnlyMapName != "")
            {
                _activitiesFromMap.Add(contextOnlyActivityName);
            }
            #endregion
            #endregion

            #region Populate the XsdName.  This identifies the name of the document we will write later
            //Get the Source XSD Name - this is the same as the MessageType - root schema type
            string XsdName = "";
            
            if (inputDocName != "" && inputDocName != null)
            {
                XsdName = BTS_MapReader.MapReader.SourceXSDName(inputDocName);
                XsdName = SourceSchemaNameSpace + "#" + XsdName;
            }
            else
            {
                //The XSDName really specifies the message schema this is to be generated for.  If you want to generate a 
                //activity for all messages this should be message agnostic
                XsdName = "AllMessageTypes";
            }
            
            #endregion

            #region Build NewBAMActivity object
            //Build Metadata file
            _newDispatcherMessage.Name = XsdName;

            //Iterate over each activity and add build a new BAM Activity and add it to the List of BAM Activities
            foreach (string ActFromMap in _activitiesFromMap)
            {
                _newBamActivity = new ParsingLogic.Types.BAMActivity();
                _newBamActivity.Name = ActFromMap;

                Types.DispatcherActivity _dispatcherActivity = new ParsingLogic.Types.DispatcherActivity();
                _dispatcherActivity.ActivityName = ActFromMap;
                _newDispatcherMessage.Activity.Add(_dispatcherActivity);

                #region Populate _activityXPATHS from Data file btm
                Hashtable _activityXPATHS = new Hashtable();
                if (inputDocName != "")
                {
                    _activityXPATHS = BTS_MapReader.MapReader.GetLinksForActivity(ActFromMap, inputDocName);
                }
                #endregion
                #region Populate _activityCTXProps from Context Map file btm
                Hashtable _activityCTXProps = new Hashtable();
                Hashtable _activityCTXOnlyProps = new Hashtable();
                Hashtable _activityUploadContext = new Hashtable();
                if (contextMapName != "")
                {
                    if (_activityCTXProps == null)
                    {
                        _activityCTXProps = new Hashtable();
                    }
                    foreach (DictionaryEntry _de in BTS_MapReader.MapReader.GetLinksForActivity(ActFromMap, contextMapName))
                    {
                        _activityCTXProps.Add(ParsingLogic.BTS_MapReader.MapReader.ParseDestinationActivity((string)_de.Key), _de.Value);
                    }
                }
                #endregion
                #region Populate _activityCTXOnlyProps from Context Only Map region
                if (contextOnlyMapName != "")
                {
                    foreach (DictionaryEntry _de in BTS_MapReader.MapReader.GetLinksForActivity(ActFromMap, contextOnlyMapName))
                    {
                        _activityCTXOnlyProps.Add(ParsingLogic.BTS_MapReader.MapReader.ParseDestinationActivity((string)_de.Key), _de.Value);
                    }
                    if (_activityCTXOnlyProps.Count == 0)
                    {
                        throw new ApplicationException("Application error.  Application expected to find some links in the provided BTM file that matched to the provided activity name.  Please ensure that you correctly named the activity in the command line and that activity is present in the supplied map with a corresponding link.");
                    }
                }
                #endregion
                #region Populate _activityUploadContext from Upload Context Map region
                if (uploadContextMapName != "")
                {
                    foreach (DictionaryEntry _de in BTS_MapReader.MapReader.GetUploadLinks(ActFromMap, UploadContextMapName))
                    {
                        _activityUploadContext.Add(ParsingLogic.BTS_MapReader.MapReader.ParseDestinationActivity((string)_de.Key), _de.Value);
                    }
                }
                #endregion

                ArrayList _methods = new ArrayList();

                #region Build the properties associated with the MessageData section of an activity - Data File provided by BTM
                if (inputDocName != "")
                {
                    //Update the map information
                    _newBamActivity.MapDLL = PartialAssemblyName;
                    _newBamActivity.MapName = CompiledMapName;
                    _newBamActivity.UseStreamingXSL = System.Convert.ToBoolean(UseStreamingXSL.ToString());
                        
                    foreach (DictionaryEntry _de in _activityXPATHS)
                    {
                        string _key = (string)_de.Key;
                        if (_key.Contains("METHOD_") || _key.Contains("CONSTANT_"))
                        {
                            _methods.Add(_de);
                            
                        }
                        else
                        {
                            string valueDelim = (string)_de.Value;
                            valueDelim = valueDelim.Split('.')[1];
                            Types.DataInformation _entry = new ParsingLogic.Types.DataInformation();
                            _entry.Name = valueDelim;
                            _entry.Name = valueDelim;
                            _entry.FullName = (string)_de.Value;
                            _entry.XPathQuery = (string)_de.Key;
                            _newBamActivity.MessageData.Add(_entry);
                        }
                    }
                }
                #endregion
                #region Build the properties associated with the MessageContext section of an activity - Context File provided by BTM
                if (contextMapName != "")
                {
                    foreach (DictionaryEntry _de in _activityCTXProps)
                    {
                        string _key = (string)_de.Key;
                        if (_key.Contains("METHOD_") || _key.Contains("CONSTANT_"))
                        {
                            _methods.Add(_de);
                        }
                        else
                        {
                            string valueDelim = (string)_de.Value;
                            valueDelim = valueDelim.Split('.')[1];
                            Types.DataInformation _info = new ParsingLogic.Types.DataInformation();
                            _info.Name = valueDelim;
                            _info.FullName = (string)_de.Value;
                            _info.XPathQuery = (string)_de.Key;
                            _newBamActivity.MessageContext.Add(_info);
                        }
                    }
                }
                #endregion
                #region Build the properties associated with the MessageContext section of an activity - Context ONLY File provided by BTM
                if (contextOnlyMapName != "")
                {
                    foreach (DictionaryEntry _de in _activityCTXOnlyProps)
                    {
                        string _key = (string)_de.Key;
                        if (_key.Contains("METHOD_") || _key.Contains("CONSTANT_"))
                        {
                            _methods.Add(_de);
                        }
                        else
                        {
                            string valueDelim = (string)_de.Value;
                            valueDelim = valueDelim.Split('.')[1];
                            Types.DataInformation _info = new ParsingLogic.Types.DataInformation();
                            _info.Name = valueDelim;
                            _info.FullName = (string)_de.Value;
                            _info.XPathQuery = (string)_de.Key;
                            _newBamActivity.GenericContext.Add(_info);
                        }
                    }
                }
                #endregion
                #region Build the properties associated with the MapMethods section of an activity - build by looking at methods retreived in the 3 regions above
                //Method Type
                foreach (DictionaryEntry _de in _methods)
                {
                    if (_de.Key.ToString().Contains("CONSTANT_"))
                    {
                        Types.DataInformation _info = new ParsingLogic.Types.DataInformation();
                        _info.Name = _de.Value.ToString();
                        _info.FullName = (string)_de.Value;
                        string[] splitted = _de.Key.ToString().Split(':');
                        _info.MethodName = @"""" + splitted[2] + @"""";

                        _newBamActivity.MapMethods.Add(_info);
                    }
                    else
                    {
                        string valueDelim = (string)_de.Value;
                        valueDelim = valueDelim.Split('.')[1];
                        Types.DataInformation _info = new ParsingLogic.Types.DataInformation();

                        _info.Name = valueDelim;
                        _info.FullName = (string)_de.Value;
                        string _key = (string)_de.Key;
                        string[] splitted = _key.Split(':');
                        _info.MethodName = splitted[1];
                        _newBamActivity.MapMethods.Add(_info);
                    }
                }
                #endregion
                #region Build the properties associated with the Upload Context section of an activity
                //Method Type
                if (uploadContextMapName != "")
                {
                    foreach (DictionaryEntry _de in _activityUploadContext)
                    {

                        Types.DataInformation _info = new ParsingLogic.Types.DataInformation();
                        _info.Name = (string)_de.Key;
                        _info.XPathQuery = (string)_de.Key;
                        _info.FullName = (string)_de.Value + "." + ActFromMap;
                        _newBamActivity.UploadContext.Add(_info);

                    }
                }
                  #endregion

            
                //Add the new bam activity to the list
                _newBAMActivities.Add(_newBamActivity);


            }
            
            #endregion

            _newDispatcherMessage.ClassName = _newBAMActivities[0].Name + "_" + ParseXSDName(XsdName);
      
            //Now merge in the changes
            Types.AllActivities _existingAllActivities = Types.AllActivities.Load(System.IO.Path.Combine(xmlMetaDataDir, allActivitiesMetadataFile));
            Types.AllDispatcherTypes _existingDispatcherMessages = Types.AllDispatcherTypes.Load(System.IO.Path.Combine(xmlMetaDataDir, dispatcherMetadataFile));
            //Iterate over all the new activities that were created and add or overwrite the changes
            foreach (Types.BAMActivity _newActivity in _newBAMActivities)
            {
                if (_existingAllActivities.ActivityExists(_newActivity.Name))
                {
                    _existingAllActivities.OverwriteBAMActivity(_newActivity);
                }
                else
                {
                    _existingAllActivities.BAMActivityList.Add(_newActivity);
                }
            }

            if (_existingDispatcherMessages.BTSMessageTypeExists(_newDispatcherMessage.ClassName))
            {
                _existingDispatcherMessages.OverwriteBTSMessageType(_newDispatcherMessage);
            }
            else
            {
                _existingDispatcherMessages.BTSMessageList.Add(_newDispatcherMessage);
            }



            #region Add ContextLibrary information to the Dispatcher if needed
            //Get the existing list of context namespaces and add them to the dispatcher if needed
            if (_existingDispatcherMessages.ContextLibraryList.Count == 0)
            {
                if (System.IO.File.Exists(System.IO.Path.Combine(xmlMetaDataDir, contextLibraryMetadataFile)))
                {
                    System.Xml.Serialization.XmlSerializer ctxXMLSerializer = new System.Xml.Serialization.XmlSerializer(typeof(List<Types.ContextLibrary>));
                    System.Xml.XmlReader _rdr = System.Xml.XmlReader.Create(System.IO.Path.Combine(xmlMetaDataDir, contextLibraryMetadataFile));
                    _existingDispatcherMessages.ContextLibraryList = (System.Collections.Generic.List<Types.ContextLibrary>)ctxXMLSerializer.Deserialize(_rdr);
                    _rdr.Close();

                }
            }
            #endregion

            string FileName = ParseXSDName(XsdName);
            //To ensure a unique file name we should try and find the first activity name and append it so we dont overwrite it.
            //This will allow you to have one incoming source but have different maps for different activities
            FileName = _newBAMActivities[0].Name  + "_" + FileName;

            //Save the objects
            _existingAllActivities.Save(System.IO.Path.Combine(xmlMetaDataDir, FileName + "_MetaData.xml"));
            _existingDispatcherMessages.Save(System.IO.Path.Combine(xmlMetaDataDir, dispatcherMetadataFile));

            


            //XSLT for Activities
            

            GenerateViaXSLT(System.IO.Path.Combine(xsltDir, "ActivityXPaths.xslt"), System.IO.Path.Combine(xmlMetaDataDir, FileName + "_MetaData.xml"), System.IO.Path.Combine(outputDir, FileName + "_BAMActivities.cs"), ParseXSDName(XsdName),XsdName);
            //XSLT for Dispatcher
            GenerateViaXSLT(System.IO.Path.Combine(xsltDir, "Dispatcher.xslt"), System.IO.Path.Combine(xmlMetaDataDir, dispatcherMetadataFile), System.IO.Path.Combine(outputDir, "Generated_Dispatcher.cs"), null,null);
            Console.WriteLine("Utility completed without error.  Press any key to continue.");
            Console.ReadLine();
        }

        //XLST Transform Stuff
        private static void GenerateViaXSLT(string xsltFileName, string xmlMetaDataPath, string outputFile, string messageType, string classType)
        {
            System.Xml.Xsl.XslCompiledTransform xslt = new System.Xml.Xsl.XslCompiledTransform();
            System.IO.StreamWriter streamWriter = null;
            System.Xml.Xsl.XsltArgumentList args = new System.Xml.Xsl.XsltArgumentList();
            if (messageType != null)
            {

                args.AddParam("MessageType", "", messageType);
            }
            if (classType != null)
            {

                args.AddParam("ClassType", "", classType);
            }


            try
            {
                System.Xml.XmlReader _rdr = new System.Xml.XmlTextReader(xmlMetaDataPath);
                streamWriter = new System.IO.StreamWriter(outputFile);
                xslt.Load(xsltFileName);
                xslt.Transform(_rdr, args, streamWriter);
                _rdr.Close();

            }
            finally
            {
                if (streamWriter != null)
                {
                    streamWriter.Flush();
                    streamWriter.Close();
                }
            }
        }

        //Cleanup XSDName
        private static string ParseXSDName(string XsdName)
        {
            string FileName = XsdName;
            //Manipulate the filename
            FileName = FileName.Replace("http://", "");
            FileName = FileName.Replace(@"/", "_");
            FileName = FileName.Replace(@"\", "_");
            FileName = FileName.Replace(@".", "_");
            FileName = FileName.Replace(@"#", "__");

            return FileName;
        }
    }
}